// LCP 39. 无人机方阵
// 在 「力扣挑战赛」 开幕式的压轴节目 「无人机方阵」中，每一架无人机展示一种灯光颜色。 无人机方阵通过两种操作进行颜色图案变换：

// 调整无人机的位置布局
// 切换无人机展示的灯光颜色
// 给定两个大小均为 N*M 的二维数组 source 和 target 表示无人机方阵表演的两种颜色图案，由于无人机切换灯光颜色的耗能很大，请返回从 source 到 target 最少需要多少架无人机切换灯光颜色。

// 注意： 调整无人机的位置布局时无人机的位置可以随意变动。

// 示例 1：
// 输入：source = [[1,3],[5,4]], target = [[3,1],[6,5]]
// 输出：1
// 解释：
// 最佳方案为
// 将 [0,1] 处的无人机移动至 [0,0] 处；
// 将 [0,0] 处的无人机移动至 [0,1] 处；
// 将 [1,0] 处的无人机移动至 [1,1] 处；
// 将 [1,1] 处的无人机移动至 [1,0] 处，其灯光颜色切换为颜色编号为 6 的灯光；
// 因此从source 到 target 所需要的最少灯光切换次数为 1。
// 8819ccdd664e91c78cde3bba3c701986.gif

// 示例 2：
// 输入：source = [[1,2,3],[3,4,5]], target = [[1,3,5],[2,3,4]]
// 输出：0
// 解释：
// 仅需调整无人机的位置布局，便可完成图案切换。因此不需要无人机切换颜色

// 提示：
// n == source.length == target.length
// m == source[i].length == target[i].length
// 1 <= n, m <=100
// 1 <= source[i][j], target[i][j] <=10^4

func minimumSwitchingTimes(source, target [][]int) (ans int) {
	// 题目没有考虑移动消耗，那就简单了，统计所有相同的数字，下一个页面相同颜色无人机直接交换位置，反正不在意消耗，到最后剩下下一个画面不需要的颜色，数量除2就是交换次数
	// 事实上结果并不在意如何移动
	cnt := map[int]int{}         // map 记录第一个图案中各个颜色无人机数量
	for i, row := range source { // 外层
		for j, v := range row { // 内层
			cnt[v]++
			cnt[target[i][j]]-- // 对第二个图案需要颜色做减法（也就是需要操作）
		}
	}
	for _, c := range cnt {
		// 为 0 是正好抵消，无人机不切换颜色，交换位置
		// 正数就是下一个页面不需要那么多同种颜色
		// 负数就是下一个页面缺少几种该颜色，不在意，无人机什么颜色都可以呈现
		ans += abs(c)
	}
	return ans / 2
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}